package dsync.SFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Class provides sync for level directory 
 * @author ilya
 * @see SFile
 */
public class SDir {

	final String default_config = "SDir.xml";
	
	private File config_File;
	private Properties SDirProperties;
	private Logger dlog;
	
	private File dstDir;
	private File sourceDir;
	
	/**
	 * default configuration file "SDir.xml"
	 * @throws IOException
	 * @throws InvalidPropertiesFormatException
	 */
	public SDir() throws IOException, InvalidPropertiesFormatException
	{
		SDirProperties = new Properties();
		config_File = new File(default_config);
		dlog = Logger.getLogger("SDir");
		dlog.addHandler(new FileHandler("SDir.log", true));
		
		if(!isSuchConfig())
		{
			createConfigFile();		
		}
		
		loadConfig();
		
		sourceDir = new File(SDirProperties.getProperty("Source_dir"));
		dstDir = new File(SDirProperties.getProperty("Destination_dir"));
		if(!(dstDir.exists()) ||
				!(sourceDir.exists()))
		{
			throw new FileNotFoundException("\n" +
					sourceDir.getAbsolutePath() + " - no such\n" + 
					dstDir.getAbsolutePath() + " - no such\n");
		}
	}
	/**
	 * configuration file not used
	 * @param source_dir path to source directory
	 * @param dst_dir path to destination directory
	 * @throws IOException
	 * @throws InvalidPropertiesFormatException
	 */
	public SDir(String source_dir, String dst_dir) throws IOException, InvalidPropertiesFormatException
	{
		dlog = Logger.getLogger("SDir");
		dlog.addHandler(new FileHandler("SDir.log", true));
		
		sourceDir = new File(source_dir);
		dstDir = new File(dst_dir);
		if(!(dstDir.exists()) || dstDir == null || sourceDir == null ||
				!(sourceDir.exists()))
		{
			throw new FileNotFoundException("\n" +
					sourceDir.getAbsolutePath() + " - no such\n" + 
					dstDir.getAbsolutePath() + " - no such\n");
		}
	}
	
	/**
	 * load settings of configuration file
	 * @param config_file configuration file
	 * @throws IOException
	 * @throws InvalidPropertiesFormatException
	 */
	public SDir(String config_file) throws IOException, InvalidPropertiesFormatException
	{
		SDirProperties = new Properties();
		config_File = new File(default_config);
		dlog = Logger.getLogger("SDir");
		dlog.addHandler(new FileHandler("SDir.log", true));
		
		if(!isSuchConfig())
		{
			createConfigFile();		
		}
		
		loadConfig(config_file);
		
		sourceDir = new File(SDirProperties.getProperty("Source_dir"));
		dstDir = new File(SDirProperties.getProperty("Destination_dir"));
		if(!(dstDir.exists()) ||
				!(sourceDir.exists()))
		{
			throw new FileNotFoundException("\n" +
					sourceDir.getAbsolutePath() + " - no such\n" + 
					dstDir.getAbsolutePath() + " - no such\n");
		}
	}
	
	private void loadConfig() throws InvalidPropertiesFormatException, IOException
	{
		config_File.setReadable(true);
		FileInputStream in = new FileInputStream(config_File);
		SDirProperties.loadFromXML(in);
		in.close();
		dlog.log(Level.INFO, "file load - " + default_config + " \n");
	}
	
	private void loadConfig(String filename) throws InvalidPropertiesFormatException, IOException
	{
		config_File.setReadable(true);
		FileInputStream in = new FileInputStream(filename);
		SDirProperties.loadFromXML(in);
		in.close();
		dlog.log(Level.INFO, "file load - " + filename + " \n");
	}
	
	private boolean isSuchConfig()
	{
		if(config_File.exists())
			return true;
		else
			return false;
	}
	
	private void createConfigFile() throws IOException
	{
		
		if(!config_File.createNewFile())
			throw new IOException("File not created");
		
		FileOutputStream out = new FileOutputStream(config_File);
		SDirProperties.setProperty("Source_dir", "x");
		SDirProperties.setProperty("Destination_dir", "x");
		SDirProperties.storeToXML(out, "Default properties");
		out.close();
		dlog.log(Level.INFO, "file created - " + default_config + " \n");
		
	}
	
	/**
	 * 
	 * @param src
	 * @param dst
	 * @return false if file not found
	 */
	boolean findFile(File src, File dst)
	{
		if(dst.isDirectory())
		{
			for(File fd : dst.listFiles())
			{
				if(src.getName().equals(fd.getName()))
				{
					return true;
				}
			}
		}
		else
		{
			if(src.getName().equals(dst.getName()))
				return true;
			else 
				return false;
		}
		
		return false;
	}
	
	private void sync(File src, File dst) throws FileNotFoundException, IOException
	{
		if(src.isDirectory())
		{
			for(File fs : src.listFiles())
			{
				for(File fd : dst.listFiles())
				{
					if(fs.getName().equals(fd.getName()))
					{
						sync(new File(fs.getAbsolutePath()), new File(fd.getAbsolutePath()));
					}
				}
			}
		}
		else
		{
			
			if(src.getName().equals(dst.getName()))
			{
				new SFile(src.getAbsolutePath()).sync(dst);
			}		
			
		}
	}
	
	private void primary_sync(File src, File dst) throws FileNotFoundException, IOException
	{
		File new_dir;
		if(src.isDirectory() && dst.isDirectory())
		{
			for(File fs : src.listFiles())
			{
				if(!fs.isDirectory())
				{
					try
					{
						copyFile(fs, dst);
					}
					catch(IOException e)
					{
						dlog.log(Level.WARNING, fs.getAbsolutePath() + " - file not copy\n");
					}
				}
				else
				{
					new_dir = new File(dst.getPath() + "/" + fs.getName());
					new_dir.mkdir();
					sync(fs, new_dir);
				}
			}
		}
		else
		{
			if(!src.isDirectory())
			{
				copyFile(src, dst);
			}
		}
	}
	
	private void copyFile(File src_file, File dst_file) throws IOException
	{
		if(!src_file.exists())
			throw new FileNotFoundException();
		
		File new_file;
		if(dst_file.isDirectory())
		{
			new_file = new File(dst_file.getPath() + "/" + src_file.getName());
		}
		else
			new_file = new File(dst_file.getAbsolutePath());
		
		if(new_file.exists()){
			if(!new_file.delete())
				throw new IOException();
		}
		
		if(!new_file.createNewFile())
			throw new IOException();
		
		FileOutputStream out = new FileOutputStream(new_file);
		FileInputStream in = new FileInputStream(src_file);
		
		long file_size;
		int i = 0;
		file_size = src_file.length(); 
		
		while(i < file_size)
		{
			out.write(in.read());
			i++;
		}
		out.close();
		in.close();
		
		dlog.log(Level.INFO, src_file.getAbsolutePath() + " - file copy success\n");
		
	}
	
	/**
	 * if boolean flag cp - true that used copy else not used copy files
	 * @param cp flag
	 * @throws IOException
	 */
	public void updateAll(boolean cp) throws IOException
	{
		if(cp)
			primary_sync(sourceDir, dstDir);
		else
			sync(sourceDir, dstDir);
	}

}
